# -*- coding: utf-8 -*-
import sys
import os
import re
from PyQt4 import QtGui, QtCore
import urllib.request
import json
import threading
import tempfile
import zipfile
import xml.dom.minidom
import configparser
import time

from aoam_interface import *

config = {
    'AOpath' : "", 
    'updateTime' : 30,
    'language': ''
}
configPath = "config.ini"
addonsSubDirectory = "data\\Mods\\Addons"
modsSubDirectory = "data\\Mods" #чтобы не вырезать путь к модам



class MainWindow(Ui_MainWindow):
    addonsList = None
    currentAddon = ''
    flag = 1 #флаг выхода из скрипта, чтобы поток автообновления не висел вечно, идеи?
    lastUpdate = 0 #время в секундах с последнего обновления аддонов
    flagAddonsListDownloading = 0 #флаг загрузки списка аддонов

    #выход из программы
    def quitApp(self):
        self.flag = 0
        print('Выход из приложения')
        self.hide() #чтобы быстро убиралось с глаз
        QtGui.qApp.quit()

    #событие нажатия на крестик
    def closeEvent(self, event):
        event.ignore() 
        self.hide()
        print('Форма скрыта')

    #начальные установки, связь сигналов и слотов
    def setup(self):
        #нажатие на кнопку аддоны
        self.actionAddons.triggered.connect(self.on_actionAddons_triggered)
        #нажатие на кнопку скачать аддоны
        self.actionDownload.triggered.connect(self.on_actionDownload_triggered)
        #нажатие на кнопку опции
        self.actionOptions.triggered.connect(self.on_actionOptions_triggered)
        #выбор пункта меню обновить список аддонов
        self.actionRefreshAddonList.triggered.connect(self.refreshAddonListThread)
        #выбор пункта меню обновить список установленных аддонов
        self.actionRefreshInstalledAddonsList.triggered.connect(self.refreshInstalledAddonsList)
        #выбор аддона в списке аддонов для скачивания
        self.listViewDownload.pressed.connect(self.on_listViewDownload_itemPressed)
        #выбор аддона в списке установленных аддонов
        self.listViewInstalled.pressed.connect(self.on_listViewInstalled_itemPressed)
        #нажатие на кнопку ссылка на обсуждение
        self.buttonLink.clicked.connect(self.on_buttonLink_clicked)
        #нажатие на кнопку скриншот
        self.buttonScreenshot.clicked.connect(self.on_buttonScreenshot_clicked)
        #нажатие на кнопку скачать
        self.buttonDownload.clicked.connect(self.on_buttonDownload_clicked)
        #нажатие на кнопку Обновить все
        self.buttonUpdateAll.clicked.connect(self.on_buttonUpdateAll_clicked)
        #нажатие на кнопку удалить
        self.buttonDelete.clicked.connect(self.on_buttonDelete)
        #нажатие на кнопку выбора папки на вкладке опций
        self.buttonSelectAOpath.clicked.connect(self.on_buttonSelectAOpath_clicked)
        #нажатие на кнопку сохранить на вкладке опций
        self.buttonSaveOptions.clicked.connect(self.on_buttonSaveOptions_clicked)
        #клик по иконке в трее
        self.trayIcon.activated.connect(self.on_iconActivated)
        #выбор пункта меню "Открыть" в контекстном меню трея
        self.actionOpenMainWindow.triggered.connect(self.on_actionOpenMainWindow_triggered)
        #выбор пункта меню "Выход" в контекстном меню трея
        self.actionExit.triggered.connect(self.quitApp)
        #событие происходит при обновлении полного инфо об аддоне
        QtCore.QObject.connect(self, QtCore.SIGNAL('fullInfoChanged(PyQt_PyObject)'), self.on_refreshFullInfo)
        #событие смены статуса установки аддона
        QtCore.QObject.connect(self, QtCore.SIGNAL('statusDownloadChanged(PyQt_PyObject)'), self.setLogText)
        #заблокировать кнопку Скачать
        QtCore.QObject.connect(self, QtCore.SIGNAL('blockDownloadButton()'), self.blockDownloadButton)
        #происходит при завершении установки
        QtCore.QObject.connect(self, QtCore.SIGNAL('installComplete()'), self.on_installComplete)
        #происходит при окончании загрузки списка доступных аддонов
        QtCore.QObject.connect(self, QtCore.SIGNAL('addonsListDownloaded()'), self.on_addonsListDownloaded)
        #происходит при изменении установленных аддонов
        QtCore.QObject.connect(self, QtCore.SIGNAL('installedAddonsUpdated()'), self.on_installedAddonsUpdated)
        
        
        #вызывает событие закрытия окна
        self.connect(self, QtCore.SIGNAL('closeEmitApp()'), QtCore.SLOT('close()'))
        #событие возникает при изменении значения времени автоапдейта на странице настроек
        self.txtUpdateTime.valueChanged.connect(self.on_txtUpdateTime_valueChanged)
        #
        self.comboBoxLanguage.currentIndexChanged.connect(self.on_comboBoxLanguage_currentIndexChanged)
        
        self.findLanguages()
        #self.loadConfig()
        self.setConfig()

        
        
        
        self.refreshAddonListThread()
        #поток автообновления стартует через 2 секунды после запуска
        t = threading.Timer(2, self.autoUpdateThread)
        t.start()

    #поток автообновления аддонов
    def autoUpdateThread(self):
        while self.flag:
            if (self.lastUpdate/60 > int(config['updateTime'])) and (int(config['updateTime']) > 0):
                self.updateAddons()
                self.lastUpdate = 0
            else:
                self.lastUpdate = self.lastUpdate + 1
            time.sleep(1)

    #обновление всех устаревших аддонов
    def updateAddons(self):
        print('Начало автообновления аддонов')
        addonsName = []
        for q in self.modelInstalled.findItems('1', QtCore.Qt.MatchFixedString ,  iNEWVERSION):
            addonsName.append(self.modelInstalled.index(q.row(), 0).data(QtCore.Qt.DisplayRole))
        for addonName in addonsName:
            print('>%s' % addonName)
            self.installAddon(addonName)
        self.emit(QtCore.SIGNAL('installedAddonsUpdated()'))
        print('Процедура автообновления успешно завершена.')  
        if(addonsName != []):
            self.trayIcon.showMessage(self.trUtf8('Процедура автообновления успешно завершена'), self.trUtf8('Следующие аддоны были обновлены:')+ '\n' + '\n'.join(addonsName) , QtGui.QSystemTrayIcon.Information, 3000)
    #нажатие на кнопку Аддоны
    def on_actionAddons_triggered(self):
        self.groupBoxAddons.setVisible(1)
        self.groupBoxDownload.setVisible(0)
        self.groupBoxInfo.setVisible(1)
        self.groupBoxOptions.setVisible(0)
        self.actionAddons.setChecked(1)
        self.actionDownload.setChecked(0)
        self.actionOptions.setChecked(0)
        self.buttonDelete.setEnabled(True)

    #нажатие на кнопку Скачать
    def on_actionDownload_triggered(self):
        self.groupBoxAddons.setVisible(0)
        self.groupBoxDownload.setVisible(1) 
        self.groupBoxInfo.setVisible(1)
        self.groupBoxOptions.setVisible(0)
        self.actionAddons.setChecked(0)
        self.actionDownload.setChecked(1)
        self.actionOptions.setChecked(0)
        self.buttonDelete.setEnabled(False)

    #нажатие на кнопку Опции
    def on_actionOptions_triggered(self):
        self.groupBoxAddons.setVisible(0)
        self.groupBoxDownload.setVisible(0)
        self.groupBoxInfo.setVisible(0)
        self.groupBoxOptions.setVisible(1)
        self.actionAddons.setChecked(0)
        self.actionDownload.setChecked(0)
        self.actionOptions.setChecked(1)

    #список доступных аддонов загружен
    def on_addonsListDownloaded(self):
        self.flagAddonsListDownloading = 0
        self.loadAvalibleAddonsList()
        self.refreshInstalledAddonsList()

    #изменено значение времени автообновления на странице настроек
    def on_txtUpdateTime_valueChanged(self, i):
        config['updateTime'] = i
 
    #нажатие на кнопку выбора пути к аллодам
    def on_buttonSelectAOpath_clicked(self):
        dir = str(QtGui.QFileDialog.getExistingDirectory(None, "Выбор папки"))
        if dir != "":
            self.txtAOpath.setText(dir)
            config['AOpath'] = dir
    
    #выбран новый язык в настройках
    def on_comboBoxLanguage_currentIndexChanged(self, i):
        #config['language'] = self.comboBoxLanguage.currentText()
        return

    #нажатие на кнопку сохранить настройки
    def on_buttonSaveOptions_clicked(self):
        self.writeConfig()

    #был изменен список установленных аддонов
    def on_installedAddonsUpdated(self):
        self.refreshInstalledAddonsList()

    #клик по иконке в трее
    def on_iconActivated(self, reason):
        if reason in (QtGui.QSystemTrayIcon.Trigger, QtGui.QSystemTrayIcon.DoubleClick):
            self.setVisible(not self.isVisible())

    #выбор пункта меню Открыть в контекстном меню трея
    def on_actionOpenMainWindow_triggered(self):
        self.show()

    #клик по элементу в списке доступных аддонов
    def on_listViewDownload_itemPressed(self, current):
        if current == None: return
        #print('Выбран новый элемент в списке доступных аддонов: %s [%s]' % (current.text(0), current.treeWidget().objectName()))
        addonName = current.sibling(current.row(), 0).data(QtCore.Qt.DisplayRole)
        print('Выбран новый элемент в списке доступных аддонов: %s' % addonName)

        self.currentAddon = addonName
        self.textInfo.setText(self.addonsList[self.currentAddon]['description'])
        self.textChanges.setText('')
        threading.Thread(None, self.loadFullInfo,args=(self.currentAddon, )).start()

    #клик по элементу в списке установленных аддонов
    def on_listViewInstalled_itemPressed(self, current):
        #print('Выбран новый элемент в списке установленных аддонов: %s [%s]' % (current.text(0), current.treeWidget().objectName()))
        if current == None: return
        addonName = current.sibling(current.row(), 0).data(QtCore.Qt.DisplayRole)
        self.currentAddon = addonName
        try:
            self.textChanges.setText('')
            self.textInfo.setText(self.addonsList[self.currentAddon]['description'])
            threading.Thread(None, self.loadFullInfo, args=(self.currentAddon, )).start()
        except:
            print('Аддона нет в списке доступных: ' + self.currentAddon)

    #нажатие на кнопку "Ссылка на обсуждение"
    def on_buttonLink_clicked(self):
        if self.currentAddon in self.addonsList:
            if 'url' in self.addonsList[self.currentAddon]:
                try: os.startfile('http://ui9.ru' + self.addonsList[self.currentAddon]['url'].replace('/about', ''))
                except: print(sys.exc_info())

    #нажатие на кнопку скриншот
    def on_buttonScreenshot_clicked(self):
        if self.currentAddon in self.addonsList:
            if 'screenshot' in self.addonsList[self.currentAddon]:
                os.startfile('http://ui9.ru' + self.addonsList[self.currentAddon]['screenshot'])
            else:
                self.trayIcon.showMessage('Ошибка', 'Подробная информация для "%s" еще не загружена или скриншот отсутствует, грустно.' % self.currentAddon, 
                                          QtGui.QSystemTrayIcon.Information, 3000)
    #полная информация об аддоне обновлена
    def on_refreshFullInfo(self, addonName):
        print('Полное инфо обновлено: ' + addonName)
        if 'log' in self.addonsList[addonName]:
            self.textChanges.setHtml("%s" % (self.addonsList[addonName]['log']))
        else:
            self.textChanges.setHtml('')

    #нажатие на кнопку Скачать
    def on_buttonDownload_clicked(self):
        if config['AOpath'] == '':
            self.trayIcon.showMessage(self.trUtf8('Ошибка'), self.trUtf8('Не указана папка установки Аллодов') , QtGui.QSystemTrayIcon.Information, 3000)
            return
        if self.currentAddon in self.addonsList:
            self.emit(QtCore.SIGNAL('blockDownloadButton()'))
            if 'downloadLink' in self.addonsList[self.currentAddon]:
                #аддоны в архиве уже находятся в папке Addons, извлекаем в модс
                path = os.path.join(config['AOpath'], modsSubDirectory)
                threading.Thread(None, self.downloadAndExtract, args=('http://ui9.ru' + self.addonsList[self.currentAddon]['downloadLink'], path)).start()
            else:
                self.emit(QtCore.SIGNAL('installComplete()'))
                QtGui.QMessageBox.critical(None, 'Ошибка', 'До сих пор не получена ссылка на скачивание, печаль.', QtGui.QMessageBox.Yes)

    #нажатие на кнопку Обновить все
    def on_buttonUpdateAll_clicked(self):
        threading.Thread(None, self.updateAddons).start()

    #нажатие на кнопку Удалить аддон    
    def on_buttonDelete(self):
        addonsName = []
        for i in self.listViewInstalled.selectedIndexes():
            if i.column() == iNAME:
                addonsName.append(i.data(QtCore.Qt.DisplayRole))
        for a in addonsName:
            print('Удаление: ' + a)
            self.deleteFolder(os.path.join(os.path.join(config['AOpath'], addonsSubDirectory), a))
        self.emit(QtCore.SIGNAL('installedAddonsUpdated()'))
        
    #удалить папку со всем содержимым
    def deleteFolder(self, folder):
        try:
            for root, dirs, files in os.walk((folder), topdown=False):
                for name in files:
                    os.remove(os.path.join(root, name))
                for name in dirs:
                    os.rmdir(os.path.join(root, name))
            os.rmdir(folder)
        except:
            print('Ошибка удаления аддона')
        print('Папка удалена: ' + folder)
    #поток обновления списка доступных аддонов
    def refreshAddonListThread(self):
        if not(self.flagAddonsListDownloading):
            self.flagAddonsListDownloading = 1
            threading.Thread(None, self.refreshAddonList).start()

    #обновление списка доступных аддонов
    def refreshAddonList(self):
        page = urllib.request.urlopen('http://ao-addons.appspot.com/list').read().decode("utf8")#.replace('\r', '').replace('\n', '').replace('\t', '')
        self.addonsList = json.loads(page)
        self.emit(QtCore.SIGNAL('addonsListDownloaded()'))

    #добавление строки в список доступных аддонов
    def addAvaliableAddon(self, name, version, author, downloads, date):
        self.modelAvailable.appendRow([QtGui.QStandardItem(name),
                                       QtGui.QStandardItem(version),
                                       QtGui.QStandardItem(author),
                                       QtGui.QStandardItem(downloads),
                                       QtGui.QStandardItem(date)])
    #наполнение списка доступных аддонов доступными аддонами
    def loadAvalibleAddonsList(self):
        if self.addonsList == None: return
        self.modelAvailable.removeRows(0, self.modelAvailable.rowCount())
        for addonName in self.addonsList.keys():
            self.addAvaliableAddon(addonName, self.addonsList[addonName]['version'], self.addonsList[addonName]['autor'],
                                    self.addonsList[addonName]['downloads'],self.addonsList[addonName]['date'])

    #лог, может
    def setLogText(self, text):
        print(text)

    #блокировка кнопки скачивания
    def blockDownloadButton(self):
        self.buttonDownload.setEnabled(0)

    #установка аддона завершена
    def on_installComplete(self):
        self.buttonDownload.setEnabled(1)
        self.buttonDownload.setText(QtGui.QApplication.translate("MainWindow", "Скачать", None, QtGui.QApplication.UnicodeUTF8))
        self.emit(QtCore.SIGNAL('installedAddonsUpdated()'))

    #TODO: сделать кеширование загруженной информации
    #загрузка полной информации к аддону
    def loadFullInfo(self, addonName):
        print('Начало загрузки полной информации для: ' + addonName)
        addon = self.addonsList[addonName]
        try:
            #выпарсиваем отсюда ссылку на скачивание, скрин, ссылку на лог изменений
            page = urllib.request.urlopen('http://ui9.ru' + addon['url']).read().decode('utf-8', 'ignore')
        except:
            print('Ошибка загрузки инфо для: ' + 'http://ui9.ru' + addon['url'])
            print(sys.exc_info())
            self.emit(QtCore.SIGNAL('fullInfoChanged()'), self.currentAddon)
            return
        #скрин <a href="/upload/0/0/TargetsManager_EDS_.jpg" rel="imageview">
        #ссылка на скачивание <p class="buttons">  <a href="/download/2480 title="
        #лог <a href="/ajax/changelog/174"
        patternScreenshot = "<a\shref=\"(/upload.+?)\"\s+rel=\"imageview\">"
        screen = re.compile(patternScreenshot, re.S + re.M + re.X)
        patternDownloadLink = "<p\sclass=\"buttons\">.*?<a\shref=\"(.+?)\"\stitle="
        downloadLink = re.compile(patternDownloadLink, re.S + re.M + re.X)
        patternLog = "<a\shref=\"(/ajax/.+?)\"\stitle="
        log =  re.compile(patternLog, re.S + re.M + re.X)
        try:
            tmpScreen = screen.search(page).group(1)
            addon['screenshot'] = tmpScreen
        except:
            print('Не нашлось скрина: ' + addonName)
        try:
            tmpLink = downloadLink.search(page).group(1)
            addon['downloadLink'] = tmpLink
        except:
            print('Не найдена ссылка на скачивание: ' + addonName)
        try:
            tmpLog = log.search(page).group(1)
            page = urllib.request.urlopen('http://ui9.ru' + tmpLog).read().decode("utf-8", 'ignore')
            patternChanges = "<div.*?>.*?<div.*?>.*?<span.*?>(.*?)</span>.*?<span.*?>.*?<span.*?>(.*?)</span>.*?</span>.*?<div.*?>(.*?)</div>"
            changes = re.compile(patternChanges,   re.S + re.M + re.X)
            listChanges = changes.findall(page)
            res = ""
            for i in listChanges:
                res = res + '<b>' + i[0] + '</b>'+ '&nbsp;&nbsp;<sup><font size=5>'+ i[1] +'</font></sup>'+ '<br>' +  i[2] + '<hr>' 
            addon['log'] = res
        except:
            print('Не найдена ссылка на лог: ' + 'http://ui9.ru' + addon['url'])
            print(sys.exc_info())
        print('Закончена загрузка полной информации для: ' + addonName)
        self.emit(QtCore.SIGNAL('fullInfoChanged(PyQt_PyObject)'), addonName)

    #установка аддона с именем addonName
    def installAddon(self, addonName):
        path = os.path.join(config['AOpath'], modsSubDirectory)
        self.loadFullInfo(addonName)
        self.downloadAndExtract('http://ui9.ru' + self.addonsList[addonName]['downloadLink'], path)
        print('Установка завершена: ' + addonName)

    #скачивание и распаковка аддона
    def downloadAndExtract(self, url, path):
        self.emit(QtCore.SIGNAL('statusDownloadChanged(PyQt_PyObject)'), 'Начата загрузка')
        try:
            file = urllib.request.urlopen(url)
            fn = ""
            with tempfile.NamedTemporaryFile(delete=False) as f:
                f.write(file.read())
                fn = f.name
            self.emit(QtCore.SIGNAL('statusDownloadChanged(PyQt_PyObject)'), 'Распаковка архива')
            z = zipfile.ZipFile(fn, 'r')
            z.extractall(path)
            z.close()
            os.unlink(fn)
        except:
            print('Ошибка во время установки:\n' + url + '\n' + path)
            print(sys.exc_info())
            QtGui.QMessageBox.critical(None, 'Ошибка', 'Произошла ошибка во время скачивания/распаковки, беда.', QtGui.QMessageBox.Yes)
        self.emit(QtCore.SIGNAL('statusDownloadChanged(PyQt_PyObject)'), 'Установка закончена: ' + url)
        self.emit(QtCore.SIGNAL('installComplete()'))

    #добавление строки в список установленных аддонов
    def addInstalledAddon(self, name, version, latest, author, downloads, new):
         self.modelInstalled.appendRow([QtGui.QStandardItem(name),
                                       QtGui.QStandardItem(version),
                                       QtGui.QStandardItem(latest),
                                       QtGui.QStandardItem(author),
                                       QtGui.QStandardItem(downloads),
                                       QtGui.QStandardItem(new)])
    #обновление списка установленных аддонов
    def refreshInstalledAddonsList(self):
        self.modelInstalled.removeRows(0, self.modelInstalled.rowCount())
        try:
            addons = [d for d in os.listdir(os.path.join(config['AOpath'], addonsSubDirectory)) if os.path.isdir(os.path.join(os.path.join(config['AOpath'], addonsSubDirectory), d))]
        except:
            #не указана папка в настройках
            print(sys.exc_info())
            try:
                self.trayIcon.showMessage('Ошибка', 'Укажите папку установки Аллодов Онлайн в настройках программы.', 
                                          QtGui.QSystemTrayIcon.Critical, 5000)
            except:
                print(sys.exc_info())
            return
        self.modelInstalled.removeRows(0, self.modelAvailable.rowCount())
        for addonName in addons:
            if not(addonName in self.addonsList):
                self.addInstalledAddon(addonName, 'r0', 'r0', self.trUtf8('Неизвестно'), '0', '0')
                continue
            try:
                revision = 'r' + xml.dom.minidom.parse(os.path.join(os.path.join(os.path.join(config['AOpath'], addonsSubDirectory), addonName), 'AddonDesc.(UIAddon).xdb')).getElementsByTagName('revision')[0].childNodes[0].nodeValue
            except:
                continue
            new = '0'
            if int(revision.replace('r', '')) < int(self.addonsList[addonName]['version'].replace('r', '')):
                print('Найдена новая версия: ' + addonName )
                new = '1'
            self.addInstalledAddon(addonName, revision, self.addonsList[addonName]['version'], self.addonsList[addonName]['autor'],
                                    self.addonsList[addonName]['downloads'], new)

    #сохранение настроек
    def writeConfig(self):
        cfg = configparser.RawConfigParser()
        cfg.add_section('Main')
        cfg.set('Main', 'AOpath', config['AOpath'])
        cfg.set('Main', 'updateTime', config['updateTime'])
        #cfg.set('Main', 'language', config['language'])
        cfg.set('Main', 'language', self.comboBoxLanguage.currentText())
        with open(configPath, 'w') as configfile:
            cfg.write(configfile)

    #загрузка настроек
    def loadConfig(self):
        cfg = configparser.RawConfigParser()
        try:
            cfg.read(configPath)
            config['AOpath'] = cfg.get('Main', 'AOpath')
            config['updateTime'] = cfg.get('Main', 'updateTime')
            config['language'] = cfg.get('Main', 'language')
        except:
            print('Ошибка загрузки конфига')
    
    #установка настроек из конфига на странице настроек
    def setConfig(self):
        try:
            self.txtAOpath.setText(config['AOpath'])
            self.txtUpdateTime.setValue(int(config['updateTime']))
            self.comboBoxLanguage.setCurrentIndex(int(self.comboBoxLanguage.findText(config['language'])))
        except:
            print('Проблема с установкой значений')
            
    #поиск доступных языков
    def findLanguages(self):
        try:
            files = os.listdir('languages');  
            #Фильтруем список  
            langs = filter(lambda x: x.endswith('.qm'), files);
            for l in langs:
                self.comboBoxLanguage.addItem(l)
        except:
            print('Не найдена папка с языками')
            
if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    QtGui.QApplication.setQuitOnLastWindowClosed(False)
    app.setStyle("Cleanlooks")
    ui = MainWindow()
#    ui.findLanguages()
    ui.loadConfig()
    try:
        translator = QtCore.QTranslator()
        translator.load('languages/' + config['language'])
        QtGui.qApp.installTranslator(translator)
    except:
        print('Ошибка загрузки языка')
    
    ui.setupUi()
    ui.setup()
    if '-r' in sys.argv:
        ui.refreshAddonList()
        ui.updateAddons()
        ui.quitApp()
        sys.exit()
    else:
        ui.trayIcon.show()
        ui.show()
        sys.exit(app.exec_())
